-- GameAchievementM
-- Created by chengb Sep/11/2015
-- 成就系统

module("GameAchievementM", package.seeall);

-- 成就配置表
local achievementTable = {};

-- 成就奖励配置表
local achievementBonusTable = {};

-- 根据规则存放配置
local listByRule = {};

-- 可领取奖励的积分
local bonusScoreList = {};

-- id映射表
local idTree = {};

-- 所有的子规则
local rules = {};

-- 本文件名
local PURE_FILE = "GameAchievementM";

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    achievementTable = {};
    achievementBonusTable = {};
    listByRule = {};
    bonusScoreList = {};

    -- 转换成就配置表
    game_achievement_CSV = get_game_achievement_CSV();
    for i = 1, #game_achievement_CSV do
        local id = game_achievement_CSV[i].id;
        achievementTable[id] = game_achievement_CSV[i];

        -- 根据规则存放
        local rule = game_achievement_CSV[i].rule;
        if listByRule[rule] == nil then
            listByRule[rule] = {};
        end
        table.insert(listByRule[rule], id);

        -- 按照父成就存放
        local parentId = game_achievement_CSV[i].parent_id;
        if parentId == 0 then
            idTree[id] = { id };
        else
            if idTree[parentId] == nil then
                idTree[parentId] = {};
            end
            table.insert(idTree[parentId], id);
        end
    end

    game_achievement_CSV = {};

    -- 转换成就奖励配置表
    game_achievement_bonus_CSV = get_game_achievement_bonus_CSV();
    for i = 1, #game_achievement_bonus_CSV do
        local score = game_achievement_bonus_CSV[i].score;
        achievementBonusTable[score] = game_achievement_bonus_CSV[i];

        table.insert(bonusScoreList, score);
    end

    -- 将可领取奖励的积分升序
    table.sort(bonusScoreList);
end

-- 初始化
function init()
    if not _init then
        loadCsv();

        -- 载入所有的规则处理子模块
        rules = LOAD_PATH("game/logic/module/game_achievement");
    end
end

-- 查询成就信息
function query(id, path)
    local m = achievementTable[id];

    if path == "" or path == nil then
        return m;
    else
        return m[path];
    end
end

-- 查询成就奖励配置
function queryBonus(score, path)
    local m = achievementBonusTable[score];

    if path == "" or path == nil then
        return m;
    else
        return m[path];
    end
end

-- 根据规则获取成就id
function getIdsByRule(id)
    return listByRule[id] or {};
end

-- 成就功能是否开启
function isFuncOpen()
    -- 第一小关是否通关
    return DungeonAreaM.isDungeonPassed(ME.user, 11);
end

-- 获取玩家成就进度
function getProgress(id)
    if not ME.user.game_achievement then
        return 0;
    end

    local m = ME.user.game_achievement[id];

    if m == nil then
        -- 没有数据就返回0
        return 0;
    end

    -- 返回进度
    return m.progress;
end

-- 更新一个成就的进度
function setProgress(id, progress)
    local m = ME.user.game_achievement[id];

    if m == nil then
        -- 新增一个成就数据
        m = { ["progress"] = progress, }
    else
        m.progress = progress
    end

    ME.user.game_achievement[id] = m;

    -- 如果已经完成了，抛出事件
    if isCompleted(id) then
        EventMgr.fire(event.GAME_ACHIV_COMPLETE, { ["id"] = id, });
    end
end

function setAchievements(buff)
    -- 先清理下旧的数据
    ME.user.game_achievement = {};

    local buffer = Buffer.create(buff);

    local offset = 1;
    local id = 0;
    local size = Buffer.size(buffer);

    -- 这个是玩家身上的成就数据
    local achievements = ME.user.game_achievement;

    while (offset <= size) do
        -- 一个成就进度占2个字节
        local progress = Buffer.getu16(buffer, offset);

        if GameAchievementM.query(id) then
            achievements[id] = { ["progress"] = progress, }
        end

        -- 继续下一个成就
        id = id + 1;
        offset = offset + 2;
    end
end

-- 打包成就数据（verify to gs）
function packGameAchievement(filter)
    local ret = {__mapping = 1, };
    for id, data in pairs(ME.user.game_achievement) do
        if data["progress"] > 0 and (not filter[id] or filter[id]["progress"] ~= data["progress"]) then
            ret[id] = data["progress"];
        end
    end

    return ret;
end

-- 尝试完成成就
function tryComplete(id, count)
    if not query(id) then
        return false;
    end

    -- 获取成就当前进度
    local curProgress = getProgress(id);

    -- 控制进度超过上限
    local maxProgress = query(id, "progress");
    local newProgress = math.min(maxProgress, curProgress + count);

    -- 更新进度
    setProgress(id, newProgress);

    return true;
end

-- 获取子规则
function queryRule(rule)
    if rules[rule] == "TBL" then
        rules[rule] = LOAD_RUN("game/logic/module/game_achievements/" .. rule);
    end

    return rules[rule];
end

-- 获取成就描述
function queryDesc(id)
    local config = query(id);
    local desc = config["desc"];

    -- 获取需求进度
    local progress = config["progress"];

    -- 获取当前进度
    local curProgress = getProgress(id);

    local f = queryRule(config["rule"]);
    if not f then
        assert(false, string.format("子模块%s不存在。", config["rule"]));
    end

    -- 调用子模块的接口获取
    if f.queryDesc then
        desc = f.queryDesc(id);
    end
    return desc
end

-- 判断某个成就是否已经完成了
function isCompleted(id)
    -- 获取当前进度
    local curProgress = getProgress(id);

    -- 获取需求进度
    local progress = query(id, "progress");

    if not progress then
        return false;
    end

    return curProgress >= progress;
end

-- 计算当前已累积的成就积分
function calcCumulateScore()
    local totalScore = 0;
    for _, id in pairs(table.keys(achievementTable)) do
        if isCompleted(id) then
            local score = query(id, "score");
        	totalScore = totalScore + score;
        end
    end
    return totalScore;
end

-- 计算当前成就的总积分
function getTotalScore(parentId)
    local totalScore = 0;

    local list = idTree[parentId];
    if list == nil then
        return 0;
    end

    for _, id in pairs(list) do
        local score = query(id, "score");
        totalScore = totalScore + score;
    end
    return totalScore;
end

-- 是否领取过某积分的奖励
function isBonusTaken(score)
    local bonusRecord = ME.user.game_achiv_bonus or {};
    if #bonusRecord == 0 then
    	return false;
    end

    return table.indexOf(bonusRecord, score) ~= -1;
end

-- 当前是否有奖励可领取
function canTakeBonus()
    local bonusInfo = calcBonus();
    return #table.keys(bonusInfo) > 0;
end

-- 计算下一个成就奖励
function calcNextBonus()
    -- 计算当前已累积的成就积分
    local cumulateScore = calcCumulateScore();

    for _, score in pairs(bonusScoreList) do
        if cumulateScore < score and not isBonusTaken(score) then
            -- 返回下一个未领取的奖励
            local bonus = queryBonus(score, "bonus");
            if type(bonus) == 'table' and #bonus > 0 then
                return { ["score"] = score, ["bonus"] = bonus,};
            end
        end
    end

    return {};
end

-- 计算可领取的成就奖励
function calcBonus()
    -- 计算当前已累积的成就积分
    local cumulateScore = calcCumulateScore();

    for _, score in pairs(bonusScoreList) do
        repeat
            if cumulateScore < score then
                -- 积分不足
                break;
            end

            if isBonusTaken(score) then
                -- 已领取过该积分的奖励
                break;
            end

            -- 找到可领取的奖励了，直接返回
            -- 注：由于这里的积分是经过排序的，因此总是优先返回积分最小的奖励
            local bonus = queryBonus(score, "bonus");
            if type(bonus) == 'table' and #bonus > 0 then
                return { ["score"] = score, ["bonus"] = bonus,};
            end

        until true
    end

    -- 当前没有可领取的奖励
    return {};
end

-- 领取成就奖励
function takeBonus()
    -- 计算可领取的奖励
    local bonusInfo = calcBonus();
    local score = bonusInfo.score;
    local bonus = bonusInfo.bonus;

    if #bonus == 0 then
        trace(PURE_FILE, "当前没有可领取的成就奖励。");
        return false;
    end

    -- 标记该积分的奖励已被领取
    local bonusRecord = ME.user.game_achiv_bonus or {};
    if table.indexOf(bonusRecord, score) == -1 then
        table.insert(bonusRecord, score);
    end

    -- 执行奖励
    for _, arr in pairs(bonus) do
        BonusM.doBonus(arr, "game_achivement_bonus");
    end

    -- 抛出事件
    EventMgr.fire(event.GAME_ACHIV_BONUS, { ["bonus"] = bonus, ["score"] = score, });

    return true;
end

------------------------
-- 根据类型获取成就
-- @param achivType  成就类型
function getIdsByType(achivType)
    local ret = {};

    -- 遍历成就树
    for parentId, list in pairs(idTree) do
        if query(parentId, "type") == achivType then
            -- 选出当前正在进行中的成就编号
            local curId = list[#list];
            for _, id in pairs(list) do
                if not isCompleted(id) then
                    curId = id;
                    break;
                end
            end
            table.insert(ret, curId);
        end
    end

    -- 排序
    table.sort(ret);

    return ret;
end

-- 查询该成就最高阶位
function getMaxRank(id)
    local config = query(id);
    if not config then
        return 0;
    end

    local parentId;
    if config["parent_id"] == 0 then
        parentId = id;
    else
        parentId = config["parent_id"];
    end

    if type(idTree[parentId]) ~= 'table' then
        return 0;
    end

    return #idTree[parentId];
end




